#include "config.h"
#include "assert.h"
#include "LCD.h"

#define FIX_VALUE(value , max) (value > max ? max : value)

#define LCD_ROWS_PER_WRITE   (8)
#define LCD_WRITE_GROUPS       (8)

#define LCD_X_SIZE               (132)
#define LCD_Y_SIZE               (LCD_WRITE_GROUPS * LCD_ROWS_PER_WRITE)

#define LCD_COMANDREG       (*(volatile unsigned char *)(0x80000000 + 0x00)) /* LCD-Command Reg */
#define LCD_DATAREG            (*(volatile unsigned char *)(0x80000000 + 0x01)) /* LCD-Data Reg */

static uint8 __sG_DisUserBuff[LCD_WRITE_GROUPS][LCD_X_SIZE];
static uint8 __sG_DisKernelBuff[LCD_WRITE_GROUPS][LCD_X_SIZE];
static uint8 __sG_fFlesh = 0;

static void LCDWriteCmd(uint8 ucCmd);
static void LCDWriteData(uint8 ucData);
void LCDInit(void);

static int __LCDBasicDraw(int iGroupNum, int iWidth, uint8 *pucBuff);
static void _DrawBitLine1BPP(int x, int y, BYTE const*p, int Diff, int xsize, const BYTE *pTrans) ;
int LCDDrawPixel(int x, int y, uint8 onXoff);
static int LCDDrawHLine(int x1, int x2, int y);
static int LCDDrawVLine(int y1, int y2, int x);
static int LCDDrawRect(int x1,  int y1, int x2, int y2);
int LCDFillRect(int x1,  int y1, int x2, int y2, LCD_PIXEL_STATE pixel);
static void LCDDrawBitmap(int x, int y, int xsize, int ysize, int BytesPerLine, const BYTE* pData, int Diff, const BYTE *pTrans);

static void LCDWriteCmd(uint8 ucCmd)
{
    LCD_COMANDREG = ucCmd;
}

static void LCDWriteData(uint8 ucData)
{
    LCD_DATAREG = ucData;
}

void LCDInit(void)
{
    int iTmp;
    
    LCDWriteCmd(0xAE);              //Set display off
    LCDWriteCmd(0x00);              //Set lower column start address
    LCDWriteCmd(0x10);              //Set higher column start address
    LCDWriteCmd(0x40);              //Set display start line
    LCDWriteCmd(0x2E);      
    LCDWriteCmd(0x81);              //Set constrast control
    LCDWriteCmd(0x8F);              
    LCDWriteCmd(0x82);
    LCDWriteCmd(0x80);
    LCDWriteCmd(0xA0);              //Set segment remap
    LCDWriteCmd(0xA6);              //Set normal display
    LCDWriteCmd(0xA8);              //Set multiplex ratio
    LCDWriteCmd(0x3F);              // 1/64
    LCDWriteCmd(0xAD);              //Master configuration   
    LCDWriteCmd(0x8E);              //External vcc supply
    LCDWriteCmd(0xC8);              //Set com scan direction
    LCDWriteCmd(0xD3);              //Set display offset
    LCDWriteCmd(0x40);
    LCDWriteCmd(0xD5);              //Set display clock divide/oscillator frequency
    LCDWriteCmd(0xF0);              //0x90
    LCDWriteCmd(0xD8);              //Set area color mode off
    LCDWriteCmd(0x05);
    LCDWriteCmd(0xD9);
    LCDWriteCmd(0xF1);
    LCDWriteCmd(0xDA);              //Set com pin configuration
    LCDWriteCmd(0x12);
    LCDWriteCmd(0x91);
    LCDWriteCmd(0x3F);
    LCDWriteCmd(0x3F);
    LCDWriteCmd(0x3F);
    LCDWriteCmd(0x3F);
    LCDWriteCmd(0xAF);              //Set display on

    memset(__sG_DisUserBuff, 0 , sizeof(__sG_DisUserBuff));
     memset(__sG_DisKernelBuff, 0 , sizeof(__sG_DisKernelBuff));
    for(iTmp = 0; iTmp < LCD_WRITE_GROUPS; iTmp ++)
    {
        __LCDBasicDraw(iTmp, LCD_X_SIZE, __sG_DisKernelBuff[iTmp]);
    }
}

static int __LCDBasicDraw(int iGroupNum, int iWidth, uint8 *pucBuff)
{
    int iTmp;
    
    if(iGroupNum > LCD_WRITE_GROUPS || iWidth > LCD_X_SIZE)
    {
        return -1;
    }

    LCDWriteCmd(0xB0 + iGroupNum);        //Set y point
    LCDWriteCmd(0x00);                            //Set x point
    LCDWriteCmd(0x10);

    for(iTmp = 0; iTmp < iWidth; iTmp ++)
    {
        LCDWriteData(*(pucBuff + iTmp));
    }

    return 0;
}

int LCDDrawPixel(int x, int y, uint8 onXoff)
{
    int iGroupNum, iValue, iOffset;
    
    if(onXoff > PIXEL_ON)
    {
        return -1;
    }

    x = FIX_VALUE(x, (LCD_X_SIZE-1));
    y = FIX_VALUE(y, (LCD_Y_SIZE-1));

    iGroupNum = y/LCD_ROWS_PER_WRITE;
    iOffset = y%LCD_ROWS_PER_WRITE;

    switch(onXoff)
    {
        case PIXEL_OFF:
            iValue = __sG_DisUserBuff[iGroupNum][x] & (~(1<<iOffset));
        break;
        case PIXEL_ON:
            iValue = __sG_DisUserBuff[iGroupNum][x] | (1<<iOffset);
        break;
    }

    __sG_DisUserBuff[iGroupNum][x]  = iValue;
    __sG_fFlesh |= (1 << iGroupNum);
    //__LCDBasicDraw(iGroupNum, x+1, __sG_DisUserBuff[iGroupNum]);
    return 0;    
}

int LCDDrawHLine(int x1, int x2, int y)
{
    int iTmp;
    
    if(x1 >= x2)
    {
        return -1;
    }

    x1 = FIX_VALUE(x1, (LCD_X_SIZE-1));
    x2 = FIX_VALUE(x2, (LCD_X_SIZE-1));

    for(iTmp = x1; iTmp <= x2; iTmp++)
    {
        LCDDrawPixel(iTmp, y, PIXEL_ON);
    }

    return 0;
}

int LCDDrawVLine(int y1, int y2, int x)
{
    int iTmp;
    
    if(y1 > LCD_Y_SIZE || y2 > LCD_Y_SIZE || y1 >= y2)
    {
        return -1;
    }
    
    y1 = FIX_VALUE(y1, (LCD_Y_SIZE-1));
    y2 = FIX_VALUE(y2, (LCD_Y_SIZE-1));

    for(iTmp = y1; iTmp <= y2; iTmp++)
    {
        LCDDrawPixel(x, iTmp, PIXEL_ON);
        //Uart0_Printf("LCDDrawPixel x = %d, y= %d\r\n", x, iTmp); 
    }

    return 0;
}

int LCDDrawRect(int x1,  int y1, int x2, int y2)
{
    int iTmp;

    if( x1 >= x2 || y1 >= y2)
    {
        return -1;
    }

    x1 = FIX_VALUE(x1, (LCD_X_SIZE-1));
    x2 = FIX_VALUE(x2, (LCD_X_SIZE-1));
    y1 = FIX_VALUE(y1, (LCD_Y_SIZE-1));
    y2 = FIX_VALUE(y2, (LCD_Y_SIZE-1));    

    LCDDrawHLine(x1,  x2, y1);
    LCDDrawHLine(x1, x2,  y2);
    LCDDrawVLine( y1 + 1, y2 - 1, x1);
    LCDDrawVLine(y1 + 1, y2 - 1, x2);
}

int LCDFillRect(int x1,  int y1, int x2, int y2, LCD_PIXEL_STATE pixel)
{
    int iTmpY, iTmpX;

    if(x1 >= x2 || y1 >= y2)
    {
        return -1;
    }

    x1 = FIX_VALUE(x1, (LCD_X_SIZE-1));
    x2 = FIX_VALUE(x2, (LCD_X_SIZE-1));
    y1 = FIX_VALUE(y1, (LCD_Y_SIZE-1));
    y2 = FIX_VALUE(y2, (LCD_Y_SIZE-1));    

    for(iTmpY = y1; iTmpY <= y2; iTmpY++)
    {
        for(iTmpX = x1; iTmpX <= x2; iTmpX++)
        {
            LCDDrawPixel(iTmpX, iTmpY, pixel);
        }
    }
}

static void _DrawBitLine1BPP(int x, int y, BYTE const*p, int Diff, int xsize, const BYTE *pTrans) 
{
    BYTE Index0, Index1;
    const BYTE aTrans[2] = {0, 1};
    
    x += Diff;

    if(!pTrans)
    {
        pTrans = aTrans;
    }

    Index0 = *(pTrans + 0);
    Index1 = *(pTrans + 1);
    x += Diff;
    
    do{
        LCDDrawPixel(x++, y, (*p & (0x80>>Diff)) ? Index1:Index0);

        if(++Diff == 8)
        {
            Diff = 0;
            p++;
        }
    }while(--xsize);
}

void LCDDrawBitmap(int x, int y, int xsize, int ysize, int BytesPerLine, const BYTE* pData, int Diff, const BYTE *pTrans)
{
    int i;

    for(i =0; i<ysize; i++)
    {
        _DrawBitLine1BPP(x, i+y, pData, Diff, xsize, pTrans);
        
        pData += BytesPerLine;
    }
}

void LCDTask()
{
    uint8 *ucSrcGropuBuff, *ucDestGroupBuff;
    int iTmp = 0;

    ucSrcGropuBuff = __sG_DisUserBuff[0];
    ucDestGroupBuff = __sG_DisKernelBuff[0];

    while(iTmp < LCD_WRITE_GROUPS)
    {
        if(((__sG_fFlesh>>iTmp)&0x01) && 
        memcmp(ucSrcGropuBuff, ucDestGroupBuff, LCD_X_SIZE))
        {
            __LCDBasicDraw(iTmp, LCD_X_SIZE, ucSrcGropuBuff);     
            memcpy(ucDestGroupBuff, ucSrcGropuBuff, LCD_X_SIZE);
            __sG_fFlesh &= (~(1 << iTmp));
        }
        
        ucSrcGropuBuff += LCD_X_SIZE;
        ucDestGroupBuff += LCD_X_SIZE;
        iTmp++;
    }
}

const tLCDDEV_API LCD_APIList = {
    LCDDrawBitmap,
    LCDDrawRect,
    LCDDrawHLine,
    LCDDrawVLine,
    LCDFillRect,
    NULL
};

